;^Z80^
; modem control information
; Coleco 300 baud direct connect modem
;      4/5/84
;
; Data port = 05EH
; Control port = 05FH
;
; Useful equates
DATAP   EQU     05EH
CTRLP   EQU     05FH
;
;     Mode instruction values
NULL    EQU     080H
;
; To generate mode byte, select one from column A
; and one from column B. See your INTEL dealer for optional
; accessories. Add these all together, and spit 'em out.
; Chef's suggestion: DEFAULT EQU  STPB1+BITS7+PEN+EP
;                    [7 bits, 1 stop bit, even parity]
;
; column A
STPB1   EQU     040H    ; 1 stop bit
STPB15  EQU     080H    ; 1 1/2 stop bits
STPB2   EQU     0C0H    ; 2 stop bits
; column B
BITS5   EQU     000H    ; 5 bits/char
BITS6   EQU     004H    ; 6 bits/char
BITS7   EQU     008H    ; 7 bits/char
BITS8   EQU     00CH    ; 8 bits/char
; optional accessories
EP      EQU     020H    ; 1=even parity, 0=odd
PEN     EQU     010H    ; 1=enable parity, 0=disable
; required options
;  ---> The only one applicable here is 300 baud!!!  <---
X64     EQU     003H    ; 64x clock rate [300 baud]
;
; the assumed default value
DEFAULT EQU     STPB2+BITS8     ; 8 bits, 2 stops, no parity
; Command instruction values
;
; Select desired functions, then add to get command byte
;
EHM     EQU     080H    ; 1=enter hunt mode [no effect on async op]
                        ; [same as NULL]
IR      EQU     040H    ; 1=internal reset
RTS     EQU     020H    ; 1=set RTS to 0, enable transmitter
ER      EQU     010H    ; 1=reset error conditions
SBRK    EQU     008H    ; 1=send break [TXD goes "low"]
RXE     EQU     004H    ; 1=receive enable
TXE     EQU     001H    ; 1=transmit enable
DTR     EQU     002H    ; set DTR to 0, seize phone line
;
; Note this important stuff  |
;                            V
; 1:    If DTR ever gets set to 0, [SZ or preset goes low] then
;       modem goes to originate mode.
; 2:    Once line is seized, DTR is set to 1, ^SZ is low, so
;       7474 has preset=H, data=L,.html clear=H. In this condition,
;       toggling clock [RTS or ^TXE] will change to answer mode
;
; So what?
;
; With phone hung up, modem is in originate mode. To seize line,
; set DTR and RTS to 1 [this drops ^SZ]. This grabs phone line
; and enables carrier.
; To switch to answer mode, set RTS to 0 then back to 1.
;
;  STATUS BITS
;
TXRDY   EQU     0               ; indicates transmitter empty
RXRDY   EQU     1               ; indicates valid character ready
TXMT    EQU     2               ; indicates buffer empty
P_ERR   EQU     3               ; set when parity error detected
OE      EQU     4               ; set on overrun error
FE      EQU     5               ; set on framing error
BD      EQU     6               ; set when break detected
DSR     EQU     7               ; indicated carrier detect
CD      EQU     DSR
;
; data areas
C_DATA  DEFB    NULL            ; current 8251 command
;
; global subroutine names
;
        GLOBAL  DELAY,DIAL,SEIZE,ANSMOD
        GLOBAL  HANGUP,CD_STAT,U_STAT
        GLOBAL  CHR_IN,CHR_OUT,M_INIT
        GLOBAL  SET_UART
;
;***************************************************************************
; SUBROUTINES
;***************************************************************************
;
; Wait 10 msec
DELAY:
        PUSH            AF              ; [3 uSEC]
        PUSH            BC              ; [3]
        PUSH            DE              ; [3]
        PUSH            HL              ; [3]
        LD              HL,0            ; [2.5]
        LD              DE,0            ; [2.5]
        LD              BC,1590
; with all the pushes and pops, we've used about 32 uSEC.
; we need a total of 2,000 LDIRs less 7
; BUT this is off a bit, so we have to go about 18% faster
        LDIR
        POP             HL              ; [3]
        POP             DE              ; [3]
        POP             BC              ; [3]
        POP             AF              ; [3]
        RET
;
; *************************************
; DIAL DIGIT
;  ENTRY: A=ASCII DIGIT 0-9
;  EXIT:  NONE
DIAL:
        PUSH            AF
        PUSH            BC
        PUSH            DE
        PUSH            HL
;thaT
        CP              'P'
        JR              Z,PAUSE
        CP              'p'
        JR              Z,PAUSE
        CP              ','             ;Comma=pause
        JR              Z,PAUSE
        CP              '9'+1
        JR              NC,BAD_DGT      ;if character >= ':' then we lose
        CP              '0'
        JR              C,BAD_DGT               ;if char < '0' then we lose
        JR              NZ,CHAR_OK
; an ASCII 0 means 10 pulses, so start with '9'+1
        LD              A,'9'+1
CHAR_OK:
        SUB             '0'
        LD              B,A                     ;number of pulses is now in B
;
PLSE_LP:
; All dialing is in originate mode. We assume that the line was
; already seized.
;
; unseize line for 18/29 of the 100 ms pulse time [60 ms]
        LD              A,RXE+TXE+RTS
        OUT             (CTRLP),A       ;PICK UP
        PUSH            BC
        LD              B,6
PLP1:
        CALL            DELAY
        DJNZ            PLP1
        POP             BC
; seize line for 11/29 of the 100 ms pulse time [40 ms]
        LD              A,RXE+TXE+RTS+DTR
        OUT             (CTRLP),A       ;PICK UP
        PUSH            BC
        LD              B,4
PLP2:
        CALL            DELAY
        DJNZ            PLP2
        POP             BC
;
; next pulse
        DJNZ            PLSE_LP

        LD              (C_DATA),A              ;SAVE LAST VALUE
;
; delay between digits
PAUSE:
        LD              B,80                    ;800 ms
PLP3:
        CALL            DELAY
        DJNZ            PLP3
BAD_DGT:
        POP             HL
        POP             DE
        POP             BC
        POP             AF
        RET

; ***************************
; SEIZE LINE
;  ENTRY: A=0 FOR ORIGINATE MODE
;         A-0FFH FOR ANSWER MODE
;  EXIT:  CARRIER IS ENABLED
;         REGS A & C GET TRASHED
SEIZE:
        LD              C,A             ; SAVE
        CALL            HANGUP
        LD              A,RXE+TXE+RTS+DTR
        LD              (C_DATA),A      ; SAVE LAST COMMAND
        OUT             (CTRLP),A       ; PICK UP
        LD              A,C
        OR              A
        RET             Z               ; ALL DONE
; *****************
; ANSWER MODE
; SWITCHES MODEM DIRECTLY TO ANSWER MODE
; Assumes modem off-hook
;    EXIT:  REG A TRASHED
ANSMOD:
; ONE FULL PULSE OF RTS LINE
        LD              A,RXE+TXE+DTR
        OUT             (CTRLP),A
        LD              A,RXE+TXE+RTS+DTR
        LD              (C_DATA),A      ; SAVE LAST COMMAND
        OUT             (CTRLP),A
        RET
; **********************
; HANGUP
;    HANGS UP PHONE. TRASHES REG A
;
HANGUP:
        LD              A,RXE+TXE+RTS
        LD              (C_DATA),A      ; SAVE LAST COMMAND
        OUT             (CTRLP),A       ; HANG UP PHONE AND SWITCH TO ORIGINATE MODE
        RET
; **********************
; CARRIER_STATUS
;   READS STATUS FROM UART AND MODEM CHIP
;       ENTRY:
;       EXIT:   NZ=CARRIER DETECT
;               Z=NO CARRIER
CD_STAT:
        IN              A,(CTRLP)
        BIT             DSR,A
        RET
; **********************
; UART_STATUS
;   CHECKS UART FOR ERRORS AND/OR DATA
;       ENTRY:
;       EXIT:   NC=NO ERRORS
;               C=DATA ERROR
;               NZ=CHARACTER PRESENT
;               Z=NO CHARACTER
U_STAT:
        IN              A,(CTRLP)
        OR              A
        JR              URT_CMN
URT_ERR:
        SCF
URT_CMN:
        BIT             RXRDY,A
        RET
; ************************
; CHARACTER_IN
;       ENTRY:
;       EXIT:   A=CHARACTER FROM MODEM
;               NC=NO ERROR
;               C=DATA ERROR

CHR_IN:
        CALL            U_STAT
        JR              Z,CHR_IN        ;WAIT FOR CHARACTER
        IN              A,(DATAP)
        OR              A
        RET
CHR_ERR:
        LD              A,(C_DATA)
        OR              ER              ;clear for errors
        OUT             (CTRLP),A
        XOR             ER              ;disable error reset
        OUT             (CTRLP),A
        LD              A,0
        RET
; *********************
; CHARACTER_OUT
;       ENTRY:  A=CHARACTER TO TRANSMIT
;       EXIT:   NONE
CHR_OUT:
        PUSH            AF
; WAIT UNTIL TRANSMITTER CLEAR
CHR_L1:
        IN              A,(CTRLP)
        BIT             TXMT,A
        JR              Z,CHR_L1
; DUMP THE SUCKER OUT
        POP             AF
        OUT             (DATAP),A
        RET
; *************************
; MODEM_INIT
;   PRESETS MODEM TO KNOWN INITIAL STATE
M_INIT:
        LD              A,NULL
        LD              (C_DATA),A      ; fool it into hanging up
        LD              A,DEFAULT       ; desired default value

; *************************
; SET_UART
;   SETS UART TO DESIRED STOP BITS, PARITY, ETC
;       ENTRY:  A=stop bits + bits/char + parity
;       EXIT:   NONE
SET_UART:
        OR              X64             ; clock rate is always 64x
        PUSH            AF
        LD              A,080H
        OUT             (CTRLP),A
        OUT             (CTRLP),A       ; get the 8251's attention
        LD              A,040H
        OUT             (CTRLP),A
        POP             AF
        OUT             (CTRLP),A       ; dump the init value out
        LD              A,(C_DATA)
        OUT             (CTRLP),A
        RET